{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- [编程常见问题](https://docs.python.org/zh-cn/3/faq/programming.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. 列表生成器\n",
    "\n",
    "## 描述\n",
    "\n",
    "下面的代码会报错，为什么？\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'x' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-1-6af7b8a44f6c>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      3\u001b[0m     \u001b[0mgen\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0m_\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mA\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgen\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-1-6af7b8a44f6c>\u001b[0m in \u001b[0;36m<genexpr>\u001b[1;34m(.0)\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;32mclass\u001b[0m \u001b[0mA\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mobject\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      2\u001b[0m     \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m     \u001b[0mgen\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0m_\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      4\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mA\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgen\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mNameError\u001b[0m: name 'x' is not defined"
     ]
    }
   ],
   "source": [
    "class A(object):\n",
    "    x = 1\n",
    "    gen = (x for _ in range(10))\n",
    "    \n",
    "print(list(A.gen))    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 答案\n",
    "\n",
    "这个问题是变量作用域问题，在 `gen=(x for _ in xrange(10))` 中 gen 是一个 `generator`, 在 `generator` 中变量有自己的一套作用域，与其余作用域空间相互隔离。因此，将会出现这样的 `NameError: name ‘x’ is not defined` 的问题，那么解决方案是什么呢？方案有很多:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n"
     ]
    }
   ],
   "source": [
    "class A(object):\n",
    "    x = 1\n",
    "    gen = (lambda x: (x for _ in range(10)))(x)\n",
    "    \n",
    "print(list(A.gen))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "或者答案可以是这样"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n"
     ]
    }
   ],
   "source": [
    "class A(object):\n",
    "    x = 1\n",
    "    gen = (A.x for _ in range(10))\n",
    "\n",
    "print(list(A.gen))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 补充说明\n",
    "\n",
    "**类(class)中的变量定义的作用域被限制在该类的代码块中，并不会扩展到其方法(methods)的代码块里**，包括其列表生成式(comprehensions) 以及生成器表达式(generator)，因为它们也是用function实现的，那意味着以下代码是错误的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'a' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-14-c831f1aba52f>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mclass\u001b[0m \u001b[0mA\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      2\u001b[0m     \u001b[0ma\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m42\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m     \u001b[0mb\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ma\u001b[0m \u001b[1;33m+\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32m<ipython-input-14-c831f1aba52f>\u001b[0m in \u001b[0;36mA\u001b[1;34m()\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;32mclass\u001b[0m \u001b[0mA\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      2\u001b[0m     \u001b[0ma\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m42\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m     \u001b[0mb\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ma\u001b[0m \u001b[1;33m+\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-14-c831f1aba52f>\u001b[0m in \u001b[0;36m<genexpr>\u001b[1;34m(.0)\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;32mclass\u001b[0m \u001b[0mA\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      2\u001b[0m     \u001b[0ma\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m42\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m     \u001b[0mb\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ma\u001b[0m \u001b[1;33m+\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m: name 'a' is not defined"
     ]
    }
   ],
   "source": [
    "class A:\n",
    "    a = 42\n",
    "    b = list((a + i for i in range(10)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. 最难的装饰器\n",
    "\n",
    "## 描述\n",
    "\n",
    "我想写一个**类装饰器**用来度量**函数/方法**运行时间"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "\n",
    "class Timeit(object):\n",
    "    def __init__(self, func):\n",
    "        self._warpped = func\n",
    "        \n",
    "    def __call__(self, *args, **kwargs):\n",
    "        start = time.time()\n",
    "        result = self._warpped(*args, **kwargs)\n",
    "        print('cost time: %s' %(time.time() - start))\n",
    "        return result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**此类装饰器能够运行在普通函数上：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cost time: 1.0001943111419678\n",
      "invoking function func\n"
     ]
    }
   ],
   "source": [
    "@Timeit\n",
    "def func():\n",
    "    time.sleep(1)\n",
    "    return 'invoking function func'\n",
    "\n",
    "print(func())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**此类装饰器运行在方法上会报错，为什么?**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "func() missing 1 required positional argument: 'self'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-74-734a7629aab4>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      6\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      7\u001b[0m \u001b[0ma\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mA\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 8\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ma\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfunc\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-72-9c5de6033dd8>\u001b[0m in \u001b[0;36m__call__\u001b[1;34m(self, *args, **kwargs)\u001b[0m\n\u001b[0;32m      8\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      9\u001b[0m         \u001b[0mstart\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 10\u001b[1;33m         \u001b[0mresult\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_warpped\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     11\u001b[0m         \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'cost time: %s'\u001b[0m \u001b[1;33m%\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtime\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtime\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m-\u001b[0m \u001b[0mstart\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     12\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mTypeError\u001b[0m: func() missing 1 required positional argument: 'self'"
     ]
    }
   ],
   "source": [
    "class A(object):\n",
    "    @Timeit\n",
    "    def func(self):\n",
    "        time.sleep(1)\n",
    "        return 'invoking function func'\n",
    "\n",
    "a = A()\n",
    "print(a.func())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 答案\n",
    "\n",
    "\n",
    "使用类装饰器后，在调用 `func` 函数的过程中其对应的 `instance` 并不会传递给 `__call__` 方法，造成其 `mehtod unbound` ,那么解决方法是什么呢？\n",
    "\n",
    "**方法一：**\n",
    "\n",
    "描述符\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "invoking function func\n"
     ]
    }
   ],
   "source": [
    "class Timeit(object):\n",
    "    def __init__(self, func):\n",
    "        self.func = func\n",
    "\n",
    "    def __call__(self, *args, **kwargs):\n",
    "        print('invoking Timer')\n",
    "\n",
    "    def __get__(self, instance, owner):\n",
    "        return lambda *args, **kwargs: self.func(instance, *args, **kwargs)\n",
    "\n",
    "\n",
    "class A(object):\n",
    "    @Timeit\n",
    "    def func(self):\n",
    "        time.sleep(1)\n",
    "        return 'invoking function func'\n",
    "\n",
    "\n",
    "a = A()\n",
    "print(a.func())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**方法二：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bar\n",
      "cost time: 1.0006234645843506\n"
     ]
    }
   ],
   "source": [
    "class Foo(object):\n",
    "    def __init__(self):\n",
    "        pass\n",
    "\n",
    "    def __call__(self, func):\n",
    "        def _call(*args,  **kwargs):\n",
    "            start = time.time()\n",
    "            result = func(*args, **kwargs)\n",
    "            print('cost time: %s' %(time.time() - start))\n",
    "            return result\n",
    "\n",
    "        return _call\n",
    "\n",
    "\n",
    "class Bar(object):\n",
    "    @Foo()\n",
    "    def bar(self, test, ids):   # bar = Foo()(bar)\n",
    "        time.sleep(1)\n",
    "        print('bar')\n",
    "\n",
    "\n",
    "Bar().bar('aa', 'ids')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. Python 调用机制\n",
    "\n",
    "## 描述\n",
    "\n",
    "Python开发者们知道 `__call__` 方法可以用来重载圆括号调用，好的，以为问题就这么简单？Naive！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "invoking __call__ from A !\n"
     ]
    }
   ],
   "source": [
    "class A(object):\n",
    "    def __call__(self):\n",
    "        print(\"invoking __call__ from A !\")\n",
    "\n",
    "a = A()\n",
    "a()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在大家可以看到 a() 似乎等价于 `a.__call__() `,好的，现在我们写出了如下的代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'invoking __call__ from lambda !'"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.__call__ = lambda : \"invoking __call__ from lambda !\"\n",
    "a.__call__()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "invoking __call__ from A !\n"
     ]
    }
   ],
   "source": [
    "a()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**为什么`a()` 没有调用 `a.__call__()` ？** (此题由 USTC 王子博前辈提出)\n",
    "\n",
    "## 答案\n",
    "\n",
    "原因在于，在Python中，**新式类(new class) 的内建特殊方法，和实例的属性字典是相互隔离的**，具体可以看看Python的官方文档对于这一情况的说明。\n",
    "\n",
    ">For new-style classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary. That behaviour is the reason why the following code raises an exception (unlike the equivalent example with old-style classes):\n",
    "\n",
    "同时官方也给出了一个例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "object of type 'C' has no len()",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-78-111f8026d65d>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[0mc\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mC\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[0mc\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__len__\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;32mlambda\u001b[0m\u001b[1;33m:\u001b[0m \u001b[1;36m5\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m \u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mc\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: object of type 'C' has no len()"
     ]
    }
   ],
   "source": [
    "class C(object):\n",
    "    pass\n",
    "\n",
    "c = C()\n",
    "c.__len__ = lambda: 5\n",
    "len(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c.__len__()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'__len__': <function __main__.<lambda>()>}"
      ]
     },
     "execution_count": 92,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 属性中有 `__len__`\n",
    "c.__dict__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "mappingproxy({'__module__': '__main__',\n",
       "              '__dict__': <attribute '__dict__' of 'C' objects>,\n",
       "              '__weakref__': <attribute '__weakref__' of 'C' objects>,\n",
       "              '__doc__': None})"
      ]
     },
     "execution_count": 91,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 然而这里没有\n",
    "type(c).__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 补充说明\n",
    "\n",
    "回到我们的例子上来，当我们在执行 `a.call=lambda: \"invoking call from lambda\"` 时，的确在我们在 `a.__dict__` 中新增加了一个 key 为 `__call__` 的 item，但是当我们执行 `a()` 时，因为涉及特殊方法的调用，因此我们的调用过程不会从 `a.__dict__` 中寻找属性，而是从 `type(a).__dict__` 中寻找属性。因此，就会出现如上所述的情况。\n",
    "\n",
    "# 4.描述符（待证明）\n",
    "\n",
    "**尚未搞清楚疑问，不必阅读此章节。**\n",
    "\n",
    "## 描述\n",
    "\n",
    "我想写一个 Exam 类，其属性 math 为 [0,100] 的整数，若赋值时不在此范围内则抛出异常，我决定用**描述符**来实现这个需求。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "90\n"
     ]
    }
   ],
   "source": [
    "class Grade(object):\n",
    "    def __init__(self):\n",
    "        self._score = 0\n",
    "\n",
    "    def __get__(self, instance, owner):\n",
    "        return self._score\n",
    "\n",
    "    def __set__(self, instance, value):\n",
    "        if 0 <= value <= 100:\n",
    "            self._score = value\n",
    "        else:\n",
    "            raise ValueError('grade must be between 0 and 100')\n",
    "\n",
    "\n",
    "class Exam(object):\n",
    "    math = Grade()\n",
    "\n",
    "    def __init__(self, math):\n",
    "        self.math = math\n",
    "\n",
    "\n",
    "niche = Exam(math=90)\n",
    "print(niche.math)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "75\n"
     ]
    }
   ],
   "source": [
    "snake = Exam(math=75)\n",
    "print(snake.math)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "grade must be between 0 and 100",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-114-40155114f2e2>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0msnake\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmath\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;36m120\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-112-f00f9360362d>\u001b[0m in \u001b[0;36m__set__\u001b[1;34m(self, instance, value)\u001b[0m\n\u001b[0;32m     10\u001b[0m             \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_score\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     11\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 12\u001b[1;33m             \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'grade must be between 0 and 100'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     13\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     14\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mValueError\u001b[0m: grade must be between 0 and 100"
     ]
    }
   ],
   "source": [
    "snake.math = 120"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "<br>\n",
    "看起来一切正常。不过这里面有个巨大的问题，尝试说明是什么问题\n",
    "为了解决这个问题，我改写了 Grade 描述符如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Grade(object):\n",
    "    def __init__(self):\n",
    "        self._grade_pool = {}\n",
    "\n",
    "    def __get__(self, instance, owner):\n",
    "        print(self._grade_pool )\n",
    "        return self._grade_pool.get(instance, None)\n",
    "\n",
    "    def __set__(self, instance, value):\n",
    "        if 0 <= value <= 100:\n",
    "            _grade_pool = self.__dict__.setdefault('_grade_pool', {})\n",
    "            _grade_pool[instance] = value\n",
    "        else:\n",
    "            raise ValueError(\"fuck\")\n",
    "\n",
    "\n",
    "class Exam(object):\n",
    "    math = Grade()\n",
    "\n",
    "    def __init__(self, math):\n",
    "        self.math = math\n",
    "\n",
    "\n",
    "# niche = Exam(math=90)\n",
    "# print(niche.math)\n",
    "\n",
    "# snake = Exam(math=75)\n",
    "# print(snake.math)\n",
    "\n",
    "# print(niche.math)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "niche = Exam(math=90)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{<__main__.Exam object at 0x0000018E7845B278>: 90}\n",
      "90\n"
     ]
    }
   ],
   "source": [
    "print(niche.math)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{<__main__.Exam object at 0x0000018E7845B278>: 90, <__main__.Exam object at 0x0000018E78454EB8>: 75}\n",
      "75\n"
     ]
    }
   ],
   "source": [
    "snake = Exam(math=75)\n",
    "print(snake.math)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{<__main__.Exam object at 0x0000018E7845B278>: 90, <__main__.Exam object at 0x0000018E78454EB8>: 75}\n",
      "90\n"
     ]
    }
   ],
   "source": [
    "print(niche.math)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "不过这样会导致更大的问题，请问该怎么解决这个问题？\n",
    "\n",
    "## 答案\n",
    "\n",
    "1.第一个问题的其实很简单，如果你再运行一次 `print(niche.math)` 你就会发现，输出值是 `75 `，那么这是为什么呢？这就要先从 Python 的调用机制说起了。我们**如果调用一个属性，那么其顺序是优先从实例的` __dict__` 里查找，然后如果没有查找到的话，那么依次查询类字典，父类字典，直到彻底查不到为止**。好的，现在回到我们的问题，我们发现，在我们的类 `Exam` 中，其 `self.math` 的调用过程是，首先在实例化后的实例的 `__dict__` 中进行查找，没有找到，接着往上一级，在我们的类 `Exam` 中进行查找，好的找到了，返回。那么这意味着，我们对于 `self.math` 的所有操作都是对于类变量` math` 的操作。因此造成变量污染的问题。那么该则怎么解决呢？很多同志可能会说，恩，在 `__set__` 函数中将值设置到具体的实例字典不就行了。\n",
    "\n",
    "那么这样可不可以呢？答案是，很明显不得行啊，至于为什么，就涉及到我们 Python 描述符的机制了，描述符指的是实现了描述符协议的特殊的类，三个描述符协议指的是 `__get__` , `__set__` , `__delete__`以及 Python 3.6 中新增的 `__set_name__` 方法，其中实现了 `__get__` 以及 `__set__` / `__delete__` / `__set_name__` 的是 `Data descriptors` ，而只实现了 `__get__` 的是 `Non-Data descriptor` 。那么有什么区别呢，前面说了， 我们如果调用一个属性，那么其顺序是优先从实例的 `__dict__` 里查找，然后如果没有查找到的话，那么一次查询类字典，父类字典，直到彻底查不到为止。 **但是，这里没有考虑描述符的因素进去，如果将描述符因素考虑进去，那么正确的表述应该是我们如果调用一个属性，那么其顺序是优先从实例的 `__dict__` 里查找，然后如果没有查找到的话，那么依次查询类字典，父类字典，直到彻底查不到为止。其中如果在类实例字典中的该属性是一个 `Data descriptors` ，那么无论实例字典中存在该属性与否，无条件走描述符协议进行调用，在类实例字典中的该属性是一个 `Non-Data descriptors` ，那么优先调用实例字典中的属性值而不触发描述符协议，如果实例字典中不存在该属性值，那么触发 `Non-Data descriptor` 的描述符协议。回到之前的问题，我们即使在 set 将具体的属性写入实例字典中，但是由于类字典中存在着 `Data descriptors` ，因此，我们在调用 `math` 属性时，依旧会触发描述符协议。\n",
    "\n",
    "2.经过改良的做法，利用 `dict` 的 key 唯一性，将具体的值与实例进行绑定，但是同时带来了**内存泄露**的问题。那么为什么会造成内存泄露呢，首先复习下我们的 `dict` 的特性，`dict` 最重要的一个特性，就是凡可 hash 的对象皆可为 key ，`dict` 通过利用的 hash 值的唯一性（严格意义上来讲并不是唯一，而是其 hash 值碰撞几率极小，近似认定其唯一）来保证 key 的不重复性，同时（敲黑板，重点来了），**dict 中的 key 引用是强引用类型，会造成对应对象的引用计数的增加，可能造成对象无法被 gc ，从而产生内存泄露。**那么这里该怎么解决呢？两种方法\n",
    "\n",
    "第一种："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Grad(object):\n",
    "    def __init__(self):\n",
    "        import weakref\n",
    "        self._grade_pool = weakref.WeakKeyDictionary()\n",
    "\n",
    "    def __get__(self, instance, owner):\n",
    "        return self._grade_pool.get(instance, None)\n",
    "\n",
    "    def __set__(self, instance, value):\n",
    "        if 0 <= value <= 100:\n",
    "            _grade_pool = self.__dict__.setdefault('_grade_pool', {})\n",
    "            _grade_pool[instance] = value\n",
    "        else:\n",
    "            raise ValueError(\"fuck\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "weakref 库中的 `WeakKeyDictionary` 所产生的字典的 key 对于对象的引用是弱引用类型，其不会造成内存引用计数的增加，因此不会造成内存泄露。同理，如果我们为了避免 value 对于对象的强引用，我们可以使用 `WeakValueDictionary` 。\n",
    "\n",
    "<br>\n",
    "第二种：\n",
    "\n",
    "在 Python 3.6 中，实现的 PEP 487 提案，为描述符新增加了一个协议，我们可以用其来绑定对应的对象：\n",
    "\n",
    "<br>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Grad(object):\n",
    "    def __get__(self, instance, owner):\n",
    "        return instance.__dict__[self.key]\n",
    "\n",
    "    def __set__(self, instance, value):\n",
    "        if 0 <= value <= 100:\n",
    "            instance.__dict__[self.key] = value\n",
    "        else:\n",
    "            raise ValueError(\"fuck\")\n",
    "\n",
    "    def __set_name__(self, owner, name):\n",
    "        self.key = name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这道题涉及的东西比较多，这里给出一点参考链接，invoking-descriptors , Descriptor HowTo Guide , PEP 487 , what`s new in Python 3.6 。\n",
    "\n",
    "<br>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5. Python 继承机制\n",
    "\n",
    "## 描述\n",
    "\n",
    "试求出以下代码的输出结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "init 5\n",
      "add 7\n",
      "mult 35\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "\n",
    "\n",
    "class Init(object):\n",
    "    def __init__(self, value):\n",
    "        self.val = value\n",
    "        print('init', self.val)\n",
    "        \n",
    "class Add2(Init):\n",
    "    def __init__(self, val):\n",
    "        super(Add2, self).__init__(val)\n",
    "        self.val += 2\n",
    "        print('add',self.val)\n",
    "        \n",
    "class Mul5(Init):\n",
    "    def __init__(self, val):\n",
    "        super(Mul5, self).__init__(val)\n",
    "        self.val *= 5\n",
    "        print('mult', self.val)\n",
    "        \n",
    "class Pro(Mul5, Add2):\n",
    "    pass\n",
    "\n",
    "class Incr(Pro):\n",
    "    csup = super(Pro)\n",
    "    def __init__(self, val):\n",
    "        self.csup.__init__(val)\n",
    "        self.val += 1\n",
    "        \n",
    "p = Incr(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "36\n"
     ]
    }
   ],
   "source": [
    "print(p.val)\n",
    "# (5 + 2) * 5 + 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 答案\n",
    "\n",
    "输出是 36 ，具体可以参考 New-style Classes , multiple-inheritance"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 6. Python 特殊方法\n",
    "\n",
    "疑问<https://www.oschina.net/question/3306142_2245351?sort=time>\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 可变类型参数\n",
    "\n",
    "可变参数的默认值，为避免出错应设为`None`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "def foo(mydict={}): \n",
    "    mydict['name'] = 12\n",
    "    return mydict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'name': 12}"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "foo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'age': 12, 'name': 12}"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "foo({'age':12})"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "190.355px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
